package com.trytech.mongoocrawler.client.transport.tcp;

import com.trytech.mongoocrawler.common.transport.protocol.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.log4j.Log4j2;

import java.net.InetAddress;
import java.util.UUID;

/**
 * Created by hp on 2017-7-10.
 */
@Log4j2
public class NettyTcpClient {
    private static ProtocolFilterChain filterChain;

    static {
        filterChain = new ProtocolFilterChain();
    }

    private String host;
    private int port;
    private NettyTcpClientEventHandler handler;
    private ChannelFuture channel;
    private EventLoopGroup group;

    private NettyTcpClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public static NettyTcpClient newInstance(String host, int port) {
        NettyTcpClient nettyTcpClient = new NettyTcpClient(host, port);
        return nettyTcpClient;
    }

    public void start() {
        //创建工作线程池
        if (group == null || group.isTerminated() || group.isShutdown()) {
            group = new NioEventLoopGroup();
        }
        try {
            //创建启动器
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.remoteAddress(this.host, this.port).group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        public void initChannel(NioSocketChannel ch) throws Exception {
                            ChannelPipeline channelPipeline = ch.pipeline();
                            channelPipeline.addLast(new ProtocolDecoder());
                            channelPipeline.addLast(new ProtocolEncoder());
                            handler = new NettyTcpClientEventHandler();
                            channelPipeline.addLast(handler);
                        }
                    });
            channel = bootstrap.connect().sync();
            channel.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 优雅退出，释放NIO线程组
            group.shutdownGracefully();
        }
    }

    public void addHandler(ProtocolHandler protocolHandler) {
        filterChain.addHandler(protocolHandler);
    }

    public class NettyTcpClientEventHandler extends SimpleChannelInboundHandler<CrawlerTransferProtocol> {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            CommandProtocol commandProtocol = new CommandProtocol();
            commandProtocol.setCommand(CommandProtocol.Command.GET_URL);
            commandProtocol.setTraceId(UUID.randomUUID().toString());
            //获取客户端数据
            String ip = InetAddress.getLocalHost().getHostAddress().toString();
            commandProtocol.setData(ip);
            CrawlerTransferProtocol<CommandProtocol> protocol = new CrawlerTransferProtocol();
            protocol.setType(ProtocolType.COMMAND.val());
            protocol.setCls(CommandProtocol.class);
            protocol.setContent(commandProtocol);
            ctx.channel().writeAndFlush(protocol).sync();
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, CrawlerTransferProtocol sourceProtocol) throws Exception {
            AbstractProtocol destProtocol = filterChain.doFilter((AbstractProtocol) sourceProtocol.getContent());
            if(destProtocol == null){
                System.out.println("结束");
            }
            if(destProtocol instanceof HtmltextProtocol) {
                CrawlerTransferProtocol<HtmltextProtocol> protocol = new CrawlerTransferProtocol();
                protocol.setType(ProtocolType.DATA.val());
                protocol.setCls(HtmltextProtocol.class);
                protocol.setContent((HtmltextProtocol) destProtocol);
                ctx.channel().writeAndFlush(protocol).sync();
            }
            if(destProtocol instanceof CommandProtocol) {
                CrawlerTransferProtocol<CommandProtocol> protocol = new CrawlerTransferProtocol();
                protocol.setType(ProtocolType.COMMAND.val());
                protocol.setCls(CommandProtocol.class);
                protocol.setContent((CommandProtocol) destProtocol);
                ctx.channel().writeAndFlush(protocol).sync();
            }
        }
    }
}
